package com.zbiti.anvil.basic.admin.gateway.selenium.util;

import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

import java.time.Duration;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static org.junit.jupiter.api.Assertions.assertEquals;

/**
 * 元素操作工具
 *
 * @author Fanyc
 * @date 2021-05-13 16:20
 */
public class ElementUtils {

    /**
     * 根地址
     */
    private final String baseUrl;

    /**
     * 浏览器对象
     */
    private final ChromeDriver chromeDriver;

    /**
     * 驱动等待器
     */
    private final WebDriverWait webDriverWait;

    // 单例
    private static ElementUtils _instance;

    /**
     * 获取单个实例
     *
     * @param baseUrl 根URL
     */
    public static void init(String baseUrl) {
        _instance = new ElementUtils(baseUrl);
    }

    /**
     * 获取单个实例
     *
     * @return 元素操作工具实例
     */
    public static ElementUtils instance() {
        return _instance;
    }

    // 私有构造函数
    private ElementUtils(String baseUrl) {
        this.baseUrl = baseUrl;
        System.setProperty("webdriver.chrome.driver", "D:\\dev\\Selenium\\chromedriver.exe");
        this.chromeDriver = new ChromeDriver();
        this.chromeDriver.manage().window().maximize();
        this.chromeDriver.manage().deleteAllCookies();
        this.chromeDriver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);

        this.webDriverWait = new WebDriverWait(chromeDriver, Duration.ofSeconds(10));
    }

    //////////////////// 查找元素 ////////////////////

    /**
     * 根据By匹配
     *
     * @param webElement 页面元素
     * @param by         查找方式
     * @return Web元素
     */
    public static WebElement by(WebElement webElement, By by) {
        return webElement != null ? webElement.findElement(by) : instance().getChromeDriver().findElement(by);
    }

    /**
     * 根据By匹配
     *
     * @param webElement 页面元素
     * @param by         查找方式
     * @return Web元素
     */
    public static List<WebElement> byAll(WebElement webElement, By by) {
        return webElement != null ? webElement.findElements(by) : instance().getChromeDriver().findElements(by);
    }

    /**
     * 根据Xpath匹配
     *
     * @param webElement      页面元素
     * @param xpathExpression xpath表达式
     * @return Web元素
     */
    public static WebElement xpath(WebElement webElement, String xpathExpression) {
        return by(webElement, By.xpath(xpathExpression));
    }

    /**
     * 根据Xpath匹配
     *
     * @param webElement      页面元素
     * @param xpathExpression xpath表达式
     * @return Web元素
     */
    public static List<WebElement> xpathAll(WebElement webElement, String xpathExpression) {
        return byAll(webElement, By.xpath(xpathExpression));
    }

    /**
     * 根据CSS选择器匹配
     *
     * @param webElement  页面元素
     * @param cssSelector CSS选择器
     * @return Web元素
     */
    public static WebElement cssSelector(WebElement webElement, String cssSelector) {
        return by(webElement, By.cssSelector(cssSelector));
    }

    /**
     * 根据CSS选择器匹配
     *
     * @param webElement  页面元素
     * @param cssSelector CSS选择器
     * @return Web元素
     */
    public static List<WebElement> cssSelectorAll(WebElement webElement, String cssSelector) {
        return byAll(webElement, By.cssSelector(cssSelector));
    }

    /**
     * 获取对话框元素
     *
     * @param dialogName 对话框名称
     * @return 对话框元素
     */
    public static WebElement elDialog(String dialogName) {
        return cssSelector(null, "div[role='dialog'][aria-label='" + dialogName + "']");
    }

    /**
     * 获取对话框Body元素
     *
     * @param elDialog 对话框元素
     * @return 对话框Body元素
     */
    public static WebElement elDialogBody(WebElement elDialog) {
        return cssSelector(elDialog, "div[class~='el-dialog__body']");
    }

    /**
     * 获取对话框Footer元素
     *
     * @param elDialog 对话框元素
     * @return 对话框Footer元素
     */
    public static WebElement elDialogFooter(WebElement elDialog) {
        return cssSelector(elDialog, "div[class~='el-dialog__footer']");
    }

    /**
     * 获取Table的所有行
     * @param table Table对象
     * @return 所有行对象
     */
    public static List<WebElement> elTableTrs(WebElement table){
        return ElementUtils.cssSelectorAll(table, "tr[class='el-table__row']");
    }


    /**
     * 获取弹出popover元素
     *
     * @return 对话框元素
     */
    public static WebElement elPopover() {
        return cssSelector(null, "div[role='tooltip'][aria-hidden='false']");
    }
    //////////////////// 发送按键 ////////////////////

    /**
     * 向页面元素发送内容
     *
     * @param webElement 页面元素
     * @param keysToSend 发送的内容
     * @throws InterruptedException Sleep异常
     */
    public static void sendKeys(WebElement webElement, CharSequence... keysToSend) throws InterruptedException {
        webElement.clear();
        webElement.sendKeys(keysToSend);
        Thread.sleep(500);
    }

    /**
     * 根据Xpath发送内容
     *
     * @param webElement      页面元素
     * @param xpathExpression xpath表达式
     * @param keysToSend      发送的内容
     * @throws InterruptedException Sleep异常
     */
    public static void sendKeysByXpath(WebElement webElement, String xpathExpression, CharSequence... keysToSend) throws InterruptedException {
        WebElement element = xpath(webElement, xpathExpression);
        sendKeys(element, keysToSend);
    }

    /**
     * 根据CSS选择器发送内容
     *
     * @param webElement  页面元素
     * @param cssSelector CSS选择器
     * @param keysToSend  发送的内容
     * @throws InterruptedException Sleep异常
     */
    public static void sendKeysByCssSelector(WebElement webElement, String cssSelector, CharSequence... keysToSend) throws InterruptedException {
        WebElement element = cssSelector(webElement, cssSelector);
        sendKeys(element, keysToSend);
    }

    //////////////////// 单击事件 ////////////////////

    /**
     * 点击元素
     *
     * @param webElement 页面元素
     * @throws InterruptedException Sleep异常
     */
    public static void click(WebElement webElement) throws InterruptedException {
        webElement.click();
        Thread.sleep(500);
    }

    /**
     * 点击元素
     *
     * @param webElement      页面元素
     * @param xpathExpression xpath表达式
     * @throws InterruptedException Sleep异常
     */
    public static void clickByXpath(WebElement webElement, String xpathExpression) throws InterruptedException {
        click(xpath(webElement, xpathExpression));
    }

    /**
     * 点击元素
     *
     * @param webElement      页面元素
     * @param xpathExpression xpath表达式
     * @throws InterruptedException Sleep异常
     */
    public static void clickByCssSelector(WebElement webElement, String xpathExpression) throws InterruptedException {
        click(cssSelector(webElement, xpathExpression));
    }

    //////////////////// 断言处理 ////////////////////

    /**
     * 验证表格一列数据的正确性
     *
     * @param webElement 页面元素
     * @param tBodyXpath tBody路径
     * @param colIndex   列号，从1开始
     * @param values     一列的值
     */
    public static void assertTableColumnResult(WebElement webElement, final String tBodyXpath, final int colIndex, String[] values) {
        for (int i = 1; i <= values.length; i++) {
            String text = xpath(webElement, String.format("%s/tr[%d]/td[%d]/div", tBodyXpath, i, colIndex)).getText();
            assertEquals(values[i - 1], text);
        }
    }

    /**
     * 等待元素隐藏
     *
     * @param webElement 元素
     * @throws InterruptedException Sleep异常
     * @return
     */
    public static void waitInvisibility(WebElement webElement) throws InterruptedException {
        if (webElement == null) {
            Thread.sleep(500);
        }
        ElementUtils.console(">> 等待元素隐藏");
        instance().getWebDriverWait()
                .pollingEvery(Duration.ofSeconds(1))
                .until(ExpectedConditions.invisibilityOf(webElement));
    }

    /**
     * 等待元素显示
     *
     * @param webElement 元素
     * @throws InterruptedException Sleep异常
     * @return
     */
    public static WebElement waitVisibility(WebElement webElement) throws InterruptedException {
        if (webElement == null) {
            Thread.sleep(500);
        }
        ElementUtils.console(">> 等待元素显示");
        return instance().getWebDriverWait()
                .pollingEvery(Duration.ofSeconds(1))
                .until(ExpectedConditions.visibilityOf(webElement));
    }

    /**
     * 等待元素加载
     *
     * @param by 元素选择器
     * @throws InterruptedException Sleep异常
     */
    public static void waitLocated(WebElement webElement, By by) throws InterruptedException {
        if (by == null) {
            Thread.sleep(500);
        }
        ElementUtils.console(">> 等待元素显示");
        instance().getWebDriverWait()
                .pollingEvery(Duration.ofSeconds(1))
                .until(ExpectedConditions.visibilityOf(by(webElement, by)));
    }

    /**
     * 等待对话框显示
     *
     * @throws InterruptedException Sleep异常
     * @return
     */
    public static WebElement waitDialogVisibility() throws InterruptedException {
        return waitVisibility(cssSelector(null, "div[class='v-modal']"));
    }

    /**
     * 等待对话框隐藏
     *
     * @throws InterruptedException Sleep异常
     */
    public static void waitDialogInvisibility() throws InterruptedException {
        waitInvisibility(cssSelector(null, "div[class='v-modal']"));
    }
    //////////////////// 通用方法 ////////////////////

    /**
     * 输出信息到console
     *
     * @param msg 信息内容
     */
    public static void console(String msg) {
        instance().getChromeDriver().executeScript("window.console.log('" + msg + "')");
    }

    /**
     * 打开页面
     *
     * @param uriHash 页面hash
     * @throws InterruptedException Sleep异常
     */
    public static void open(String uriHash) throws InterruptedException {
        instance().getChromeDriver().get(instance().getBaseUrl() + uriHash);
        Thread.sleep(2000);
    }

    /**
     * 获取baseUrl
     *
     * @return 返回baseUrl
     */
    public String getBaseUrl() {
        return baseUrl;
    }

    /**
     * 获取chromeDriver
     *
     * @return 返回chromeDriver
     */
    public ChromeDriver getChromeDriver() {
        return chromeDriver;
    }

    /**
     * 获取webDriverWait
     *
     * @return 返回webDriverWait
     */
    public WebDriverWait getWebDriverWait() {
        return webDriverWait;
    }
}
